home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Maclife 157
/
MACLIFE157-2001-09.ISO.7z
/
MACLIFE157-2001-09.ISO
/
Linux
/
MacOS Tools
/
BootX 1.2.2
/
Sources
/
lib
/
MoreOSUtils
/
MoreOSUtils.c
next >
Wrap
Text File
|
2001-07-23
|
11KB
|
371 lines
/*
File: MoreOSUtils.c
Contains: A OS utility library.
Written by: Quinn
Copyright: Copyright ゥ 1998 by Apple Computer, Inc., all rights reserved.
You may incorporate this Apple sample source code into your program(s) without
restriction. This Apple sample source code has been provided "AS IS" and the
responsibility for its operation is yours. You are not permitted to redistribute
this Apple sample source code as "Apple sample source code" after having made
changes. If you're going to re-distribute the source, we require that you make
it clear in the source that the code was descended from Apple sample source
code, but that you've made changes.
Change History (most recent first):
<2> 16/3/99 Quinn Rolled in InterfaceDisableLib from DTS Technote 1137.
<1> 1/3/99 Quinn First checked in.
*/
/////////////////////////////////////////////////////////////////////
// MoreIsBetter Setup
#include "MoreSetup.h"
// Mac OS interfaces
#include <Traps.h>
#include <Gestalt.h>
#include <CodeFragments.h>
#include <MixedMode.h>
// MIB Prototypes
#include "MoreInterfaceLib.h"
// Our Prototypes
#include "MoreOSUtils.h"
/////////////////////////////////////////////////////////////////////
// We require Universal Interfaces 3.2 because earlier versions
// either mess up the prototype for FlushCodeCacheRange, or don't
// define it for CFM code, or both!
#if UNIVERSAL_INTERFACES_VERSION < 0x0320
#error MoreOSUtils.c requires Universal Interfaces 3.2 or higher.
#endif
/////////////////////////////////////////////////////////////////////
#pragma mark ----- 68K Code Cache Flush -----
// Define the _vCacheFlush trap (used by FlushCodeCache), which is not included
// in Universal Interfaces.
enum {
_vCacheFlush = 0xA0BD
};
#if TARGET_CPU_68K
// The following routines are used to flush the cache using direct
// 680x0 instructions. They are only used if no OS support is available.
//
// Because these routines take no parameters and return no results,
// they are equally effective for both classic 68K and CFM-68K.
static void FlushCacheViaCACR(void) =
// FlushCacheViaCACR is an inline assembly routine that flushes both the
// instruction and data caches by writing directly to the CACR. Used
// as a last resort by MakeData68KExecutable on 68020 and 68030.
{
0x4E7A, 0x0002, // MOVEC CACR,D0
0x08C0, 0x0003, // BSET #3,D0
0x4E7B, 0x0002 // MOVEC D0,CACR
};
static void FlushCacheWithCPushA(void) =
// FlushCacheWithCPushA is another inline assembly routine that flushes caches
// on the MC68040 using the CPushA instruction. Used as a last resort
// by MakeData68KExecutable on 68040.
{
0x4E71, // NOP ; to clear pending writes
0xF4F8 // CPUSHA BC
};
#endif
extern pascal OSStatus MakeData68KExecutable(void *address, ByteCount count)
// See comment in interface part.
{
OSErr err;
#if TARGET_CPU_PPC
MoreAssertQ(GetOSTrapAddress(_HWPriv) != GetToolTrapAddress(_Unimplemented));
#endif
// Step 1. If we have _HWPriv, try calling FlushCodeCacheRange. If that
// returns an error, call FlushCodeCache. Two important assumptions:
//
// a) any machine that has FlushCodeCacheRange implemented will necessarily
// implement FlushCodeCache.
// b) PowerPC computers always have FlushCodeCacheRange implemented, so
// we don't need Mixed Mode glue for FlushCodeCache because we'll never
// need it on a PowerPC.
if ( GetOSTrapAddress(_HWPriv) != GetToolTrapAddress(_Unimplemented) ) {
err = MoreFlushCodeCacheRange(address, count);
#if TARGET_CPU_68K
if (err != noErr) {
MoreAssertQ(GetOSTrapAddress(_vCacheFlush) != GetToolTrapAddress(_Unimplemented));
FlushCodeCache();
}
#else
MoreAssertQ(err == noErr);
#endif
// Step 2. If we don't have _HWPriv, look to see whether _vCacheFlush
// (ie FlushCodeCache) is implemented. If it is, we'll just call it.
} else if ( GetOSTrapAddress(_vCacheFlush) != GetToolTrapAddress(_Unimplemented) ) {
// The call to FlushCodeCache is conditionalised because
// Universal Interfaces does not export the call unless
// you're generating 68K code. *sigh* But that's OK because
// all PowerPC machines have _HWPriv implemented, so this
// code won't run.
#if TARGET_CPU_68K
FlushCodeCache();
#else
MoreAssertQ(false);
#endif
// Step 3. Finally, if neither of these traps is implemented, we're just
// going to execute 680x0 instructions directly. Note that we have to
// execute different instructions based on the 680x0 variant.
} else {
// Obviously, this is only going to work (or indeed compile) if we're
// generating 68K code. That's cool, because if we're generating PowerPC
// code, we must end up running on a PowerPC, and that always has
// FlushCodeCacheRange so we never get here.
#if TARGET_CPU_68K
{
UInt32 gestaltResponse;
if (Gestalt(gestaltProcessorType, (SInt32 *) &gestaltResponse) == noErr) {
if (gestaltResponse >= gestalt68020) {
if (gestaltResponse <= gestalt68030) {
FlushCacheViaCACR();
} else {
FlushCacheWithCPushA();
}
}
}
}
#else
MoreAssertQ(false);
#endif
}
// Basically this routine can't fail or, more accurately,
// there are no expected failure cases. So we always return
// noErr. In fact, the only reason this routine is defined to
// return an error code is for symmetry with MakeDataPowerPCExecutable,
// which has to return an error code in the classic 68K case.
return noErr;
}
/////////////////////////////////////////////////////////////////////
#pragma mark ----- PowerPC Code Cache Flush -----
#if TARGET_CPU_68K && !TARGET_RT_MAC_CFM
// This chunk of code implements the classic 68K case for MakeDataPowerPCExecutable,
// ie we're running classic 68K code that's generating PowerPC instructions. Boy,
// is this a pain to implement. We have to connect up to Interface, find the
// appropriate symbol, build a routine descriptor for it, and then call it.
enum {
uppMakeDataExecutableProcInfo = kPascalStackBased |
STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(void *))) |
STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(unsigned long)))
};
static CFragConnectionID gInterfaceLib = nil;
static UniversalProcPtr gMakeDataExecutable = nil;
typedef pascal void (*MakeDataExecutableProcPtr)(void *address, unsigned long count);
static pascal OSStatus MakeDataPowerPCExecutableFromClassic(void *address, ByteCount count)
{
OSErr err;
Ptr junkMain;
Str255 junkMessage;
CFragSymbolClass junkClass;
ProcPtr routinePtr;
MoreAssertQ(GetToolTrapAddress(_CodeFragmentDispatch) != GetToolTrapAddress(_Unimplemented));
MoreAssertQ(GetToolTrapAddress(_MixedModeDispatch) != GetToolTrapAddress(_Unimplemented));
err = noErr;
if (gMakeDataExecutable == nil) {
// We're not connected to InterfaceLib let, let's connect, find the MakeDataExecutable
// symbol, and build a routine descriptor for it.
err = GetSharedLibrary("¥pInterfaceLib", kPowerPCCFragArch, kLoadCFrag, &gInterfaceLib, &junkMain, junkMessage);
if (err == noErr) {
err = FindSymbol(gInterfaceLib, "¥pMakeDataExecutable", (Ptr *) &routinePtr, &junkClass);
}
if (err == noErr) {
MoreAssertQ(junkClass == kTVectorCFragSymbol);
gMakeDataExecutable = NewRoutineDescriptorTrap(routinePtr, uppMakeDataExecutableProcInfo, kPowerPCISA);
if (gMakeDataExecutable == nil) {
err = memFullErr;
}
}
// If any of this failed, let's shut it all down gracefully.
if (err != noErr) {
TermMoreOSUtils();
}
}
// If we have a UPP for MakeDataExecutable, call it.
if (gMakeDataExecutable != nil) {
((MakeDataExecutableProcPtr) gMakeDataExecutable)(address, count);
}
return err;
}
extern pascal void TermMoreOSUtils(void)
// See comment in interface part.
{
OSErr junk;
if ( gMakeDataExecutable != nil ) {
DisposeRoutineDescriptorTrap(gMakeDataExecutable);
gMakeDataExecutable = nil;
}
if ( gInterfaceLib != nil ) {
junk = CloseConnection(&gInterfaceLib);
MoreAssertQ(junk == noErr);
gInterfaceLib = nil;
}
}
#endif
extern pascal OSStatus MakeDataPowerPCExecutable(void *address, ByteCount count)
// See comment in interface part.
{
// Note: We don't have to worry about the CFM-68K case because
// CFM-68K won't run on PowerPC machines, so there's no sense
// in trying to make some data PowerPC-executable from CFM-68K code.
#if TARGET_RT_MAC_CFM
#if TARGET_CPU_68K
#pragma unused(address)
#pragma unused(count)
MoreAssertQ(false);
return noErr;
#else
MakeDataExecutable(address, count);
return noErr;
#endif
#else
return MakeDataPowerPCExecutableFromClassic(address, count);
#endif
}
/////////////////////////////////////////////////////////////////////
#pragma mark ----- Interrupt Enable and Disable -----
#if GENERATINGPOWERPC
// PowerPC Specific Code
// On PPC, we use MixedMode to handle moving the PPC parameters
// into the right 68K registers and back again. This make our
// 68K very easy to write.
enum {
kGetSRProcInfo = kRegisterBased
| RESULT_SIZE(SIZE_CODE(sizeof(UInt16)))
| REGISTER_RESULT_LOCATION(kRegisterD0),
kSetSRProcInfo = kRegisterBased
| RESULT_SIZE(0)
| REGISTER_ROUTINE_PARAMETER(1, kRegisterD0, SIZE_CODE(sizeof(UInt16)))
};
// We define the 68K as a statically initialised data structure.
// The use of MixedMode to call these routines makes the routines
// themselves very simple.
static UInt16 gGetSR[] = {
0x40c0, // move sr,d0
0x4e75 // rts
};
static UInt16 gSetSR[] = {
0x46c0, // move d0,sr
0x4e75 // rts
};
static UInt16 GetSR(void)
// Returns the current value of the SR, interrupt mask
// and all! This routine uses MixedMode to call the gGetSR data
// structure as if it was 68K code (which it is!).
{
return CallUniversalProc( (UniversalProcPtr) &gGetSR, kGetSRProcInfo);
}
static void SetSR(UInt16 newSR)
// Returns the value of the SR, including the interrupt mask and all
// the flag bits. This routine uses MixedMode to call the gGetSR data
// structure as if it was 68K code (which it is!).
{
CallUniversalProc( (UniversalProcPtr) &gSetSR, kSetSRProcInfo, newSR);
}
#else
// Classic 68K and CFM-68K Specific Code
// On classic 68K (and CFM-68K) we can simply access the
// 68K SR register using some inline procedures.
static UInt16 GetSR(void) = {
0x40c0 // move sr,d0
};
#pragma parameter SetSR(__D0)
static void SetSR(UInt16 newSR) = {
0x46c0 // move d0,sr
};
#endif
extern pascal UInt16 GetInterruptMask(void)
// See comment in header file.
{
return (GetSR() >> 8) & 7;
}
extern pascal UInt16 SetInterruptMask(UInt16 newMask)
// See comment in header file.
{
UInt16 currentSR;
MoreAssertQ(newMask >= 0 && newMask <= 7);
currentSR = GetSR();
SetSR( (currentSR & 0xF8FF) | (newMask << 8) );
return (currentSR >> 8) & 7;
}